// portScanner.js
// const net = require('net');
const dgram = require('dgram');
const portfinder = require('portfinder');
const { Mutex } = require('async-mutex');

// 全局记录最近使用的端口，避免短期重用
const recentPorts = new Set();

// 锁，保护 recentPorts 的并发访问
const portSetMutex = new Mutex();

/**
 * 检查 UDP 端口是否可用（使用 exclusive 绑定）
 */
function isUdpPortAvailable(port) {
  return new Promise((resolve) => {
    const socket = dgram.createSocket('udp4');
    socket.once('error', () => {
      socket.close(() => resolve(false));
    });
    socket.bind(
      {
        port,
        exclusive: true,
      },
      () => {
        socket.close(() => resolve(true));
      },
    );
  });
}

/**
 * 尝试抢占一个端口（加锁保护 recentPorts）
 * @param {number} port
 * @returns {boolean} 是否抢占成功
 */
async function tryReservePort(port) {
  return await portSetMutex.runExclusive(() => {
    if (recentPorts.has(port)) {
      console.error('端口已被占用:', port);
      return false;
    }
    recentPorts.add(port);
    setTimeout(() => {
      portSetMutex.runExclusive(() => recentPorts.delete(port));
    }, 300_000); // 5分钟后释放
    return true;
  });
}

/**
 * 查找指定端口范围中未占用的端口
 * @param {number} startPort - 起始端口（包含）
 * @param {number} endPort - 结束端口（包含）
 * @param {"tcp"|"udp"|"both"} protocol - 协议类型
 * @returns {Promise<number>} - 返回第一个空闲端口，若未找到则返回 -1
 */
async function findFreePort(startPort, endPort, protocol = 'both') {
  // 检查协议类型是否有效
  const validProtocols = ['tcp', 'udp', 'both'];
  if (!validProtocols.includes(protocol)) {
    throw new Error(`无效协议: ${protocol}. 请使用 'tcp', 'udp' 或 'both'`);
  }

  /* 由于需要抢占端口以判断端口是否真的可用 ，该过程中需要加锁以防止并发冲突，所以下面的代码注释，统一用后面的循环检测端口是否可用
  // 特殊处理纯 TCP 情况,使用 portfinder
  if (protocol === 'tcp') {
    try {
      const port = await portfinder.getPortPromise({
        port: startPort,
        stopPort: endPort,
        avoidPorts: Array.from(recentPorts),
      });

      // 标记为最近使用
      recentPorts.add(port);
      setTimeout(() => recentPorts.delete(port), 300_000); // 5分钟后清除
      return port;
    } catch (err) {
      console.error(`在 TCP 端口 ${startPort} 到 ${endPort} 范围内未找到可用端口: ${err.message}`);
      return -1; // 未找到可用端口
    }
  }
  */

  for (let port = startPort; port <= endPort; port++) {
    // 跳过最近使用过的端口
    if (recentPorts.has(port)) continue;

    let tcpFree = true; //用于标记当前循环到的端口是否可用于 TCP 协议
    let udpFree = true; //用于标记当前循环到的端口是否可用于 UDP 协议

    // 使用 portfinder 检查 TCP 端口
    if (protocol === 'both' || protocol === 'tcp') {
      try {
        // portfinder 的内部检查方法更可靠
        await portfinder.getPortPromise({
          port,
          stopPort: port, // 只检查单个端口
          avoidPorts: Array.from(recentPorts),
        });
      } catch {
        tcpFree = false;
      }
    }

    // 检查 UDP 端口
    if ((protocol === 'both' && tcpFree) || protocol === 'udp') {
      udpFree = await isUdpPortAvailable(port);
    }

    // 找到可用端口
    if (
      (protocol === 'udp' && udpFree) ||
      (protocol === 'both' && tcpFree && udpFree) ||
      (protocol === 'tcp' && tcpFree)
    ) {
      if (await tryReservePort(port)) {
        return port;
      }
    }
  }
  console.error(`在 ${protocol} 端口 ${startPort} 到 ${endPort} 范围内未找到可用端口`);
  return -1; // 未找到可用端口
}

module.exports = { findFreePort };
